let W = null

//6.封装一个Watcher类
class Watcher {
    constructor(obj , key){
        //Watcher实例化的时候接受两个参数,一个是key所在的对象 一个是key
        this.obj = obj,
        this.key = key,
        //每当我们获取数据就把当前的watcher保存在全局变量上
        W = this
        //获取值
        this.get();
        //在get中用完W,就把W给设置回去
        W = null;
    }

    //组件内new Watcher的时候就是想得到一个值,所以封装一个get方法拿值
    get(){
        //一旦这样拿值就会进入key的响应式处理函数中
        return this.obj[this.key];
    }

    //在watcher内部封装一个update方法,当通知我更新的时候,直接调用当前的update方法
    update(){
        console.log(
            "gogogo,有人通知我了,我重新去请求数据是:" +
              this.get() +
              ",然后开始重新渲染视图!!!!"
        );
    }
}

//5.定义一个Dep类,用来收集依赖 和 通知更新
class Dep {
    constructor(){
        //每次实例化Dep就会创建一个空的数组,然后用来保存所有的watcher
        this.subs = [];
    }

    //给Dep的实例添加两个方法用来监听数据的读取和修改

    //---如果是读取数据就收集watcher添加到subs 
    depend(watcher){
        //添加watcher到subs中
        this.subs.push(watcher)
        console.log(
            "每次访问我的数据的时候,我就把watcher收集到dep.subs这个数组中",
            this.subs
        );
    }

    //---如果是修改数据就通知所有的watcher重新加载新的数据
    notify(){
        console.log("通知所有的watcher 重新加载新的数据");
        //遍历subs中的watcher 让所有的watcher调用更新视图方法
        this.subs.forEach((watcher) => {
            watcher.update();
        });
    }
}

//4.封装defineReactive函数 对单个数据做数据劫持(数据劫持的核心代码)
function defineReactive(_data , key , value){
    //把value值再次交给observe方法,进行深层次的数据响应式(不需要判断,因为observe中有判断,如果是基本类型,则直接return,我们继续向下)
    observe(value)

    //每次对一个值进行响应式的时候实例化一个dep类
    const dep = new Dep()

    Object.defineProperty(_data , key , {
        get(){
            console.log("正在获取" + key + "的值", "收集所有依赖项watcher");
            //当这个数据正在被请求的时候 把观察者(watcher)收集到dep.subs中
            //我们可能不是模板解析的时候获取数据(也就是不是watcher来要数据) 所以要判断处理
            if (W) {
                dep.depend(W);
            }
            return value;
        },
        set(newValue){
            //如果没改就不执行
            if(newValue === value) return

            console.log("正在设置" + key + "的值");
            value = newValue
            //每当我们响应式数据被修改的时候通知所有的dep.subs中的watcher进行更新
            dep.notify()
        }
    })
}

//3.封装一个Observer类，主要用来处理发布订阅模式的核心操作（响应式操作：收集依赖，通知更新）
class Observer {
    //构造器接收的是要被响应式处理的数据
    constructor(_data){
        //把这个对象放在实例上,将来在任何原型方法中都可以访问到这个实例上的数据
        this._data = _data

        //判断当前的数据是对象还是数组
        if(Array.isArray(_data)){
            //是数组就执行处理数组的方法
            this.observerArr(_data)
        }else{
            
            //是对象就执行处理对象的方法
            this.walk(_data)
        }
    }

    //在Observer的原型对象上封装一个对数组操作的方法
    observerArr(_data){
        //当_data时数组的时候 不能直接进行defineReactive操作 但是我们要对数组内部的对象进行响应式操作
        _data.forEach((item) => {
            //把数组的值交给observe函数重新监听
            observe(item)
        })
    }

    //在Observer的原型对象上封装一个对对象操作的方法
    walk(_data){
        //遍历对象中的属性 把每一个数据的响应式核心代码交给defineReactive
        for(let key of Object.keys(_data)){
            
            defineReactive(_data , key , _data[key])
        }
    }

}

//2.封装一个observe函数 接收_data , 遍历_data的值 , ！！！一个个！！！进行数据劫持
function observe(_data){
    //判断_data如果不是对象则直接return 不需要深层次响应式处理
    if(typeof _data !== "object" || _data === null) return

    //把数据给Observer类处理响应式操作
    new Observer(_data)
}

//1.封装一个Vue的构造函数
function Vue(options) {
    //把options对象中的data,放在Vue实例化对象vm上的_data上
    this._data = options.data

    //【进行数据代理】 把data中的属性取出来 , 一个个放在vue实例上
    for (let key of Object.keys(this._data)) {
        //把属性一个个在实例(this)上放一份并设置存取器
        Object.defineProperty(this, key, {
            get() {
                //读vm上的属性的时候去_data中拿值
                return this._data[key]
            },
            set(newValue) {
                //修改vm上的属性的时候把改后的值给_data一份
                this._data[key] = newValue
            }
        })
    }



    //拿到_data的数据开始进行响应式操作 , 我们首先把响应式操作代码放在observe函数中
    observe(this._data)

}

//实例化Vue
const vm = new Vue({
    data: {
        count: 1,
        person: {
            name: "老王",
            age: 18
        },
    }
})

console.log(vm, "vm");

//获取数据
function huoqu(){
    //模拟模板解析,每个组件都会实例化一个watcher 然后开始观察数据
    new Watcher(vm , "count")
    new Watcher(vm , "count")
    new Watcher(vm , "person")
}

//设置数据
function shezi(){
    vm.count++
}

huoqu()
// shezi()